#!/usr/bin/env python
# coding:utf-8
import json
from hashlib import md5

from twisted.python import log
from toughadmin.tools import utils,mcache
from toughadmin.tools.utils import safestr
from toughadmin.console.admin.base import BaseHandler
from toughadmin.tools.permit import permit
from toughadmin.console.admin import (radius_form, portal_form)
from toughadmin.console import models
import logging

def mksign(secret, params=[], debug=True):
    """ 生成消息签名
    :param secret:
    :param params:
    :param debug:
    :return: :rtype:
    """
    _params = [str(p) for p in params if p is not None]
    _params.sort()
    _params.insert(0, secret)
    strs = ''.join(_params)
    if debug:
        log.msg("[mksign] ::::::: sign_src = %s" % strs)
    mds = md5(strs.encode()).hexdigest()
    return mds.upper()


def check_sign(secret, msg,debug=True):
    """ 校验消息签名
    :param secret:
    :param msg: dict type  data
    :return: :rtype: boolean
    """
    if "sign" not in msg:
        return False
    sign = msg['sign']
    params = [msg[k] for k in msg if k != 'sign']
    local_sign = mksign(secret, params)
    if debug:
        log.msg("[check_sign] ::::::: remote_sign = %s ,local_sign = %s" % (sign, local_sign), level=logging.DEBUG)
    return sign == local_sign


class ApiHandler(BaseHandler):
    def check_xsrf_cookie(self):
        pass

    def get_active_policy_server(self):
        """查询可用的策略服务器
        """
        return self.db.query(models.TraPolicyServer).filter_by(status=1).first()

    def parse_request(self):
        try:
            msg_src = self.request.body
            if self.settings.debug:
                log.msg("[api debug] :::::::: (%s) request body : %s" % (self.request.path, utils.safestr(msg_src)))
            req_msg = json.loads(msg_src)
        except Exception as err:
            log.err('parse params error %s' % utils.safestr(err))
            raise ValueError("parse params error")

        if not check_sign(self.settings['api_secret'],req_msg):
            raise ValueError("message sign error")

        return req_msg


class RadiusPingHandler(ApiHandler):
    """ radius 链路检测
    """

    def post(self):
        try:
            req_msg = self.parse_request()
        except Exception as err:
            self.render_json(msg=safestr(err.message))
            return

        name = req_msg.get('name')
        ipaddr = req_msg.get('ip_addr') or self.request.remote_ip

        # check radius exists
        radius = self.db.query(models.TraRadius).filter_by(name=name, ip_addr=ipaddr).first()
        if not radius:
            return self.render_json(code=100, msg=u'radius node not exists')
        else:
            radius.last_check = utils.get_currtime()
            self.db.commit()

        return self.render_json(code=0, msg=u'pong')


class StatusReportHandler(ApiHandler):
    """ radius 状态上报
    """
    def post(self):
        try:
            req_msg = self.parse_request()
        except Exception as err:
            self.render_json(msg=safestr(err.message))
            return

        name = req_msg.get('name')
        auth_all = req_msg.get('auth_all')
        auth_accept = req_msg.get('auth_accept')
        auth_reject = req_msg.get('auth_reject')
        acct_all = req_msg.get('acct_all')
        acct_start = req_msg.get('acct_start')
        acct_stop = req_msg.get('acct_stop')
        acct_update = req_msg.get('acct_update')
        acct_on = req_msg.get('acct_on')
        acct_off = req_msg.get('acct_off')

        status = self.db.query(models.TraStatus).filter_by(radius_name=name).first()
        if not status:
            stat = models.TraStatus()
            stat.radius_name = name
            stat.auth_all = auth_all
            stat.auth_accept = auth_accept
            stat.auth_reject = auth_reject
            stat.acct_all = acct_all
            stat.acct_start = acct_start
            stat.acct_stop = acct_stop
            stat.acct_update = acct_update
            stat.acct_on = acct_on
            stat.acct_off = acct_off
            self.db.add(stat)
        else:
            status.auth_all = auth_all
            status.auth_accept = auth_accept
            status.auth_reject = auth_reject
            status.acct_all = acct_all
            status.acct_start = acct_start
            status.acct_stop = acct_stop
            status.acct_update = acct_update
            status.acct_on = acct_on
            status.acct_off = acct_off

        self.db.commit()
        return self.render_json(code=0, msg=u'success')

class RadiusAddHandler(ApiHandler):
    """ radius 接入
    """

    def post(self):
        try:
            req_msg = self.parse_request()
        except Exception as err:
            self.render_json(msg=safestr(err.message))
            return

        name = req_msg.get('name')
        ip_addr = req_msg.get('ip_addr') or self.request.remote_ip
        admin_port = req_msg.get('admin_port')
        secret = req_msg.get('secret')
        auth_port = req_msg.get('auth_port')
        acct_port = req_msg.get('acct_port')

        vform = radius_form.radius_add_vform()
        if not vform.validates(dict(
            name=name,
            ip_addr=ip_addr,
            admin_port=admin_port,
            secret=secret,
            auth_port=auth_port,
            acct_port=acct_port)):
            return self.render_json(code=1, msg=vform.errors)

        radius = models.TraRadius()
        radius.name = name
        radius.ip_addr = ip_addr
        radius.secret = secret
        radius.admin_port = admin_port
        radius.auth_port = auth_port
        radius.acct_port = acct_port
        radius.status = 0
        radius.last_check = utils.get_currtime()

        self.db.add(radius)
        self.db.commit()
        return self.render_json(code=0, msg=u'success')


class PortalPingHandler(ApiHandler):
    """ portal 链路检测
    """
    def post(self):
        try:
            req_msg = self.parse_request()
        except Exception as err:
            self.render_json(msg=safestr(err.message))
            return

        name = req_msg.get('name')
        ipaddr = req_msg.get('ip_addr') or self.request.remote_ip

        # check radius exists
        portal = self.db.query(models.TraPortal).filter_by(name=name, ip_addr=ipaddr).first()
        if not portal:
            return self.render_json(code=100, msg=u'portal node not exists')
        else:
            portal.last_check = utils.get_currtime()
            self.db.commit()

        return self.render_json(code=0, msg=u'pong')


class PortalAddHandler(ApiHandler):
    """ portal 接入
    """

    def post(self):
        try:
            req_msg = self.parse_request()
        except Exception as err:
            self.render_json(msg=safestr(err.message))
            return

        name = req_msg.get('name')
        ip_addr = req_msg.get('ip_addr') or self.request.remote_ip

        if self.db.query(models.TraPortal).filter_by(name=name, ip_addr=ip_addr).count() > 0:
            return self.render_json(code=0, msg=u'portal exists')

        admin_port = req_msg.get('admin_port')
        secret = req_msg.get('secret')
        http_port = req_msg.get('http_port')
        listen_port = req_msg.get('listen_port')
        ac_server = req_msg.get('ac_server')

        vform = portal_form.portal_add_vform()
        _data = dict(
            name=name,
            ip_addr=ip_addr,
            admin_port=admin_port,
            secret=secret,
            http_port=http_port,
            listen_port=listen_port,
            ac_server=ac_server
        )

        if not vform.validates(_data):
            return self.render_json(code=1, msg=vform.errors)

        portal = models.TraPortal()
        portal.name = name
        portal.ip_addr = ip_addr
        portal.admin_port = admin_port
        portal.secret = secret
        portal.http_port = http_port
        portal.listen_port = listen_port
        portal.ac_server = ac_server
        portal.status = 0
        portal.last_check = utils.get_currtime()

        self.db.add(portal)
        self.db.commit()
        return self.render_json(code=0, msg=u'success')


class BasQueryHandler(ApiHandler):
    """ 接入设备查询
    """

    def post(self):
        try:
            req_msg = self.parse_request()
        except Exception as err:
            self.render_json(msg=safestr(err.message))
            return

        bas_list = self.db.query(models.TraBas)
        result = dict(code=0, msg='success', bas_list=[])
        for bas in bas_list:
            result['bas_list'].append([
                bas.ip_addr,
                bas.bas_name,
                bas.bas_secret,
                bas.bas_type,
                bas.time_type,
                bas.coa_port
            ])
        self.render_json(**result)

class PolicyConfigHandler(ApiHandler):
    """ radius策略服务器配置信息获取
    """
    def post(self):
        try:
            req_msg = self.parse_request()
        except Exception as err:
            self.render_json(msg=safestr(err.message))
            return

        policy_server = self.get_active_policy_server()

        if not policy_server:
            return dict(code=1,msg='error')

        result = dict(
            code=0,
            msg='success',
            policy_server=policy_server.policy_server_ip,
            auth_port=policy_server.policy_auth_port,
            acct_port=policy_server.policy_acct_port,
            secret=policy_server.secret
        )
        self.render_json(**result)


class TplQueryHandler(ApiHandler):
    """ 模板查询
    """
    def query_tpl_by_ssid(self,ssid):
        domain_code = self.db.query(models.TraSsid.domain_code).filter_by(ssid=ssid).scalar()
        tpl_name = self.db.query(models.TraDomain.tpl_name).filter_by(domain_code=domain_code).scalar()
        tpl_name = tpl_name or "default"
        tpl_dict = dict(
            tpl_name=tpl_name,
            bind_ssid=ssid,
            domain=domain_code
        )
        tpattrs = self.db.query(models.TraTemplateAttr).filter_by(tpl_name=tpl_name)
        for attr in tpattrs:
            tpl_dict[attr.attr_name] = attr.attr_value

        dmattrs = self.db.query(models.TraDomainAttr).filter_by(domain_code=domain_code)
        for dattr in dmattrs:
            tpl_dict[dattr.attr_name] = dattr.attr_value

        return dict(code=0, msg='success', attrs=tpl_dict)

    def query_tpl_by_gwid(self, gwid):
        domain_code = self.db.query(models.TraGwid.domain_code).filter_by(gwid=gwid).scalar()
        tpl_name = self.db.query(models.TraDomain.tpl_name).filter_by(domain_code=domain_code).scalar()
        tpl_name = tpl_name or "default"
        tpl_dict = dict(
            tpl_name=tpl_name,
            bind_gwid=gwid,
            domain=domain_code
        )
        tpattrs = self.db.query(models.TraTemplateAttr).filter_by(tpl_name=tpl_name)
        for attr in tpattrs:
            tpl_dict[attr.attr_name] = attr.attr_value

        dmattrs = self.db.query(models.TraDomainAttr).filter_by(domain_code=domain_code)
        for dattr in dmattrs:
            tpl_dict[dattr.attr_name] = dattr.attr_value

        return dict(code=0, msg='success', attrs=tpl_dict)


    def post(self):
        try:
            req_msg = self.parse_request()
        except Exception as err:
            self.render_json(msg=safestr(err.message))
            return
        ssid = req_msg.get('ssid')
        gwid = req_msg.get('gwid')
        if ssid:
            self.render_json(**self.query_tpl_by_ssid(ssid))
        elif gwid:
            self.render_json(**self.query_tpl_by_gwid(gwid))


class DomainQueryHandler(ApiHandler):
    """ 域信息查询
    """
    def query_domain_by_ssid(self,ssid):
        mssid = self.db.query(models.TraSsid).filter_by(ssid=ssid).first()
        if not mssid:
            log.msg("query domain : default")
            return dict(code=1, msg=u"ssid do not exist use default config", domain="default")

        log.msg("query domain :%s" %mssid.domain_code)
        return dict(code=0, msg='success',domain=mssid.domain_code)


    def query_domain_by_gwid(self, gwid):
        mgwid = self.db.query(models.TraGwid).filter_by(gwid=gwid).first()
        if not mgwid:
            log.msg("query domain : None")
            return dict(code=1, msg=u"gwid do not exist use default config", domain="")

        log.msg("query domain :%s" % mgwid.domain_code)
        return dict(code=0, msg='success', domain=mgwid.domain_code)


    def post(self):
        try:
            req_msg = self.parse_request()
        except Exception as err:
            self.render_json(msg=safestr(err.message))
            return
        ssid = req_msg.get('ssid')
        gwid = req_msg.get('gwid')
        if ssid:
            self.render_json(**self.query_domain_by_ssid(ssid))
        elif gwid:
            self.render_json(**self.query_domain_by_gwid(gwid))


class OSTypesQueryHandler(ApiHandler):
    """ 设备类型信息查询
    """
    def post(self):
        try:
            req_msg = self.parse_request()
        except Exception as err:
            self.render_json(msg=safestr(err.message))
            return

        ostypes = [(it.os_name, it.dev_type, it.match_rule) for it in self.db.query(models.TraOSTypes)]
        self.render_json(code=0, msg='success', rules=ostypes)


permit.add_handler(RadiusPingHandler, r"/api/radius/ping")
permit.add_handler(RadiusAddHandler, r"/api/radius/add")
permit.add_handler(PortalPingHandler, r"/api/portal/ping")
permit.add_handler(PortalAddHandler, r"/api/portal/add")
permit.add_handler(BasQueryHandler, r"/api/bas/query")
permit.add_handler(TplQueryHandler, r"/api/tpl/query")
permit.add_handler(OSTypesQueryHandler, r"/api/ostype/query")
permit.add_handler(DomainQueryHandler, r"/api/domain/query")
permit.add_handler(StatusReportHandler, r"/api/radius/status")
permit.add_handler(PolicyConfigHandler, r"/api/radius/policy")
permit.add_handler(PolicyConfigHandler, r"/api/plserver/query")